﻿using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.AspNetCore.Mvc.Formatters;
using RJCP.IO.Ports;
using ST.Data;
using ST.ModelVM.ProtocolDefine;
using ST.ModelVM.ProtocolSimHost;
using ST.Simulator.Tasks;

namespace ST.Simulator.Trans
{
    public class PortTrans : AbstractTrans
    {
        public ILogger<PortTrans> _logger;
        SerialPortStream listenSocket = new SerialPortStream();

        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token;
        public PortTrans(ILogger<PortTrans> _logger)
        {
            this._logger = _logger;
        }
        public override void Start(ProtocolSim sim)
        {
            protocolSim = sim;
            token = cts.Token;
            Task.Run(() => StartListen());
            base.Start(sim);
        }

        public override void Stop()
        {
            try
            {
                cts.Cancel();


                if (listenSocket != null)
                {
                    listenSocket.Close();
                    listenSocket = null;
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Stop error");
            }

            base.Stop();
        }


        byte[] buffer = new byte[1024];
        DateTime LstOpenDt = DateTime.MinValue;
        bool Open()
        {
            if ((DateTime.Now - LstOpenDt).TotalSeconds < 5)
            {
                return false;
            }

            listenSocket = new SerialPortStream();
            listenSocket.PortName = protocolSim.ComPort;
            listenSocket.BaudRate = protocolSim.BaudRate;
            listenSocket.Parity = protocolSim.Parity switch
            {
                "None" => Parity.None,
                "Odd" => Parity.Odd,
                "Even" => Parity.Even,
                "Mark" => Parity.Mark,
                "Space" => Parity.Space,
            };
            listenSocket.DataBits = protocolSim.DataBits;
            listenSocket.StopBits = protocolSim.StopBits switch
            {
                "One" => StopBits.One,
                "OnePointFive" => StopBits.One5,
                "Two" => StopBits.Two,

            };
            listenSocket.Open();
            LstOpenDt = DateTime.Now;
            return IsOpen();
        }
        bool IsOpen()
        {
            return listenSocket != null && listenSocket.IsOpen;
        }
        public async void StartListen()
        {
            try
            {
                while (!token.IsCancellationRequested)
                {
                    try {
                        if (!IsOpen())
                        {
                            Open();
                        }
                        if (listenSocket.BytesToRead > 0)
                        {
                            Read();
                        }
                        await Task.Delay(50, token);
                    }
                    catch (Exception ex2)
                    {
                        if (token.IsCancellationRequested)
                        {
                            break;
                        }
                        _logger.LogError(ex2.Message ?? "");
                        _logger.LogError(ex2.StackTrace ?? "");
                    }


                }
            }
            catch (Exception ex1)
            {
                _logger.LogError(ex1.Message ?? "");
                _logger.LogError(ex1.StackTrace ?? "");
                throw;
            }
        }
        public void Read()
        {
            byte[] receivedData = new byte[listenSocket.BytesToRead];
            listenSocket.Read(receivedData);


            if (IsModbusTcp(receivedData))
            {
                var trd = new PortReqData(receivedData);
                var mhost = TransTask.Instance.lData.Where(a => a.ProtocolSimId == protocolSim.Id && a.HostId == receivedData[0]).FirstOrDefault();
                if (trd.unitId == mhost.HostId)
                {
                    if (trd.functionCode == 5 || trd.functionCode == 6 || trd.functionCode == 10)
                    {
                        listenSocket.Write(receivedData);
                        TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "发送", receivedData,
                           $"应答指令 {trd.functionCode}");

                        return;
                    }
                    var StartAddress = trd.StartAddress;
                    var EndAddress = trd.StartAddress + trd.ReqNum;
                    var cmd = mhost.GDSJ.Where(a => a.Gnm == trd.functionCode
                    && a.StartAddress <= StartAddress
                    && a.EndAddress >= EndAddress
                    ).FirstOrDefault();
                    if (cmd != null)
                    {
                        HandeGd(listenSocket, cmd, trd, mhost, receivedData);
                        return;
                    }
                    var cmd2 = mhost.XHSJ.Where(a => a.Gnm == trd.functionCode
                   && a.StartAddress <= StartAddress
                   && a.EndAddress >= EndAddress
                   ).FirstOrDefault();
                    if (cmd2 != null)
                    {
                        HandeXh(listenSocket, cmd2, trd, mhost, receivedData);
                        return;
                    }
                }
                TransTask.Instance.AddSimLog(protocolSim, 0, "接收", receivedData, "无效数据");
            }
            else
            {
                TransTask.Instance.AddSimLog(protocolSim, 0, "接收", receivedData, "无效数据");
            }


        }



        private bool IsModbusTcp(byte[] data)
        {
            // Modbus TCP协议最小长度为7字节
            if (data.Length < 7) return false;

            // 事务标识符（通常为0）
            // 协议标识符（Modbus TCP固定为0）
            // 长度字段（包括单元标识符和功能码）
            // 单元标识符（通常为1）
            return true;
        }
        void HandeGd(SerialPortStream handler, ProtocolGDSJVM cmd, PortReqData trd, ProtocolSimHostVM mhost, byte[] receivedData)
        {
            var crc16rev = ST.Core.Util.ByteUtil.CalculateModbusCRC16(receivedData, 0, receivedData.Length - 2);
            if (crc16rev[0] != receivedData[receivedData.Length - 2]
                || crc16rev[1] != receivedData[receivedData.Length - 1]
                )
            {
                TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "接收", receivedData,
                                  $"CRC校验失败");
                return;

            }

            TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "接收", receivedData,
                                    $"请求 {trd.StartAddress} 个数{trd.ReqNum}");
            var StartAddress = trd.StartAddress;
            var EndAddress = trd.StartAddress + trd.ReqNum;

            byte[] senddata = null;
            if (cmd.BitData)
            {

            }
            else
            {
                var index = (StartAddress - cmd.StartAddress) * 2;
                var len = trd.ReqNum * 2;
                senddata = new byte[len];
                Array.Copy(cmd.Bytes, index, senddata, 0, len);
            }
            var sendData2 = new byte[senddata.Length + 5];
            sendData2[0] = receivedData[0];
            sendData2[1] = receivedData[1];  
            sendData2[2] = Convert.ToByte((senddata.Length  ) % 256);
            Array.Copy(senddata, 0, sendData2, 3, senddata.Length);

            var crc16 = ST.Core.Util.ByteUtil.CalculateModbusCRC16(sendData2, 0, senddata.Length + 3);
            sendData2[sendData2.Length - 2] = crc16[0];
            sendData2[sendData2.Length - 1] = crc16[1];



            handler.Write(sendData2);
            TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "发送", sendData2,
               $"应答 {trd.StartAddress} 有效数据{senddata.Length} 字节：{sendData2.Length}");
        }


        void HandeXh(SerialPortStream handler, ProtocolXHSJVM cmd, PortReqData trd, ProtocolSimHostVM mhost, byte[] receivedData)
        {
            var crc16rev = ST.Core.Util.ByteUtil.CalculateModbusCRC16(receivedData, 0, receivedData.Length - 2);
            if (crc16rev[0] != receivedData[receivedData.Length - 2]
                || crc16rev[1] != receivedData[receivedData.Length - 1]
                )
            {
                TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "接收", receivedData,
                                  $"CRC校验失败");
                return;

            }

            TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "接收", receivedData,
                                    $"请求 {trd.StartAddress}个数 {trd.ReqNum}");
            var StartAddress = trd.StartAddress;
            var EndAddress = trd.StartAddress + trd.ReqNum;

            byte[] senddata = null;
            if (cmd.BitData)
            {

            }
            else
            {
                var index = (StartAddress - cmd.StartAddress) * 2;
                var len = trd.ReqNum * 2;
                senddata = new byte[len];
                Array.Copy(cmd.Bytes, index, senddata, 0, len);
            }
            var sendData2 = new byte[senddata.Length + 5];
            sendData2[0] = receivedData[0];
            sendData2[1] = receivedData[1];
            sendData2[2] = Convert.ToByte((senddata.Length  ) % 256);
            Array.Copy(senddata, 0, sendData2, 3, senddata.Length);

            var crc16 = ST.Core.Util.ByteUtil.CalculateModbusCRC16(sendData2, 0, senddata.Length + 3);
            sendData2[sendData2.Length - 2] = crc16[0];
            sendData2[sendData2.Length - 1] = crc16[1];


            handler.Write(sendData2);
            TransTask.Instance.AddSimLog(protocolSim, mhost.Id, "发送", sendData2,
               $"应答 {trd.StartAddress} 有效数据{senddata.Length} 字节：{sendData2.Length}");

        }

    }
}
